home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir37 / ms_sh23s.zip / SRC / SH2.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  22KB  |  1,008 lines

  1. /*
  2.  * MS-DOS SHELL - Symantic Parser
  3.  *
  4.  * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
  5.  *
  6.  * This code is based on (in part) the shell program written by Charles
  7.  * Forsyth and the subsequence modifications made by Simon J. Gerraty (for
  8.  * his Public Domain Korn Shell) and is subject to the following copyright
  9.  * restrictions:
  10.  *
  11.  * 1.  Redistribution and use in source and binary forms are permitted
  12.  *     provided that the above copyright notice is duplicated in the
  13.  *     source form and the copyright notice in file sh6.c is displayed
  14.  *     on entry to the program.
  15.  *
  16.  * 2.  The sources (or parts thereof) or objects generated from the sources
  17.  *     (or parts of sources) cannot be sold under any circumstances.
  18.  *
  19.  *
  20.  *    $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh2.c,v 2.12 1994/08/25 20:49:11 istewart Exp $
  21.  *
  22.  *    $Log: sh2.c,v $
  23.  *    Revision 2.12  1994/08/25  20:49:11  istewart
  24.  *    MS Shell 2.3 Release
  25.  *
  26.  *    Revision 2.11  1994/02/01  10:25:20  istewart
  27.  *    Release 2.3 Beta 2, including first NT port
  28.  *
  29.  *    Revision 2.10  1994/01/11  17:55:25  istewart
  30.  *    Release 2.3 Beta 0 patches
  31.  *
  32.  *    Revision 2.9  1993/08/25  16:03:57  istewart
  33.  *    Beta 225 - see Notes file
  34.  *
  35.  *    Revision 2.8  1993/07/02  10:21:35  istewart
  36.  *    224 Beta fixes
  37.  *
  38.  *    Revision 2.7  1993/06/14  11:00:12  istewart
  39.  *    More changes for 223 beta
  40.  *
  41.  *    Revision 2.6  1993/06/02  09:52:35  istewart
  42.  *    Beta 223 Updates - see Notes file
  43.  *
  44.  *    Revision 2.5  1993/02/16  16:03:15  istewart
  45.  *    Beta 2.22 Release
  46.  *
  47.  *    Revision 2.4  1993/01/26  18:35:09  istewart
  48.  *    Release 2.2 beta 0
  49.  *
  50.  *    Revision 2.3  1992/11/06  10:03:44  istewart
  51.  *    214 Beta test updates
  52.  *
  53.  *    Revision 2.2  1992/09/03  18:54:45  istewart
  54.  *    Beta 213 Updates
  55.  *
  56.  *    Revision 2.1  1992/07/10  10:52:48  istewart
  57.  *    211 Beta updates
  58.  *
  59.  *    Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
  60.  *    MS-Shell 2.0 Baseline release
  61.  *
  62.  */
  63.  
  64. #include <sys/types.h>
  65. #include <sys/stat.h>
  66. #include <stdio.h>
  67. #include <stddef.h>
  68. #include <signal.h>
  69. #include <errno.h>
  70. #include <setjmp.h>
  71. #include <string.h>
  72. #include <ctype.h>
  73. #include <unistd.h>
  74. #include <limits.h>
  75. #include <dirent.h>
  76. #include "sh.h"
  77.  
  78. static C_Op * F_LOCAL    ScanPipeSyntax (int);
  79. static C_Op * F_LOCAL    ScanAndOrSyntax (void);
  80. static C_Op * F_LOCAL    CommandList (void);
  81. static IO_Actions * F_LOCAL SynchroniseIOList (int);
  82. static void F_LOCAL    CheckNextTokenIS (int, int);
  83. static void F_LOCAL    SyntaxError (char *);
  84. static C_Op * F_LOCAL    ScanNestedSyntax (int, int);
  85. static C_Op * F_LOCAL    ScanSimpleCommand (int);
  86. static C_Op * F_LOCAL    GetDoDoneCommandList (void);
  87. static C_Op * F_LOCAL    ThenPartList (void);
  88. static C_Op * F_LOCAL    ElsePartList (void);
  89. static C_Op * F_LOCAL    CaseList (void);
  90. static C_Op * F_LOCAL    CaseListEntries (void);
  91. static char ** F_LOCAL    GetINWordList (void);
  92. static C_Op * F_LOCAL    SetupTreeNode (int, C_Op *, C_Op *, char **);
  93. static C_Op * F_LOCAL    CreateTreeNode (void);
  94. static C_Op * F_LOCAL    yyparse (void);
  95. static char * F_LOCAL    LookUpToken (int);
  96. static int F_LOCAL    GetNextToken (int);
  97. static int F_LOCAL    LookAtNextToken (int);
  98. static char * F_LOCAL    MemoryDup (char *, size_t);
  99.  
  100. static char        *LIT_2ManyRedir = "Too many redirections";
  101. static char        *LIT_Expecting = "%s - Expecting '%s', found '%s'";
  102.  
  103. /*
  104.  * Special strings for (( and ))
  105.  */
  106.  
  107. static char        LIT_ODP [] = {
  108.     WORD_CHAR,
  109.     CHAR_OPEN_PARATHENSIS,
  110.     WORD_CHAR,
  111.     CHAR_OPEN_PARATHENSIS,
  112.     0
  113. };
  114.  
  115. static char        LIT_CDP [] = {
  116.     WORD_CHAR,
  117.     CHAR_CLOSE_PARATHENSIS,
  118.     WORD_CHAR,
  119.     CHAR_CLOSE_PARATHENSIS,
  120.     0
  121. };
  122.  
  123. /* Special [[ */
  124.  
  125. static char        LIT_ODB [] = {
  126.     WORD_CHAR,
  127.     CHAR_OPEN_BRACKETS,
  128.     WORD_CHAR,
  129.     CHAR_OPEN_BRACKETS,
  130.     0
  131. };
  132.  
  133. /*
  134.  * Other statics
  135.  */
  136.  
  137. static bool        reject;        /* GetNextToken(cf) gets symbol    */
  138.                     /* again            */
  139. static int        symbol;        /* yylex value            */
  140.  
  141. #define NEWNODE        ((C_Op *)CreateTreeNode ())
  142. #define    REJECT        (reject = TRUE)
  143. #define    ACCEPT        (reject = FALSE)
  144.  
  145. /*
  146.  * Get the next token from input
  147.  */
  148.  
  149. static int F_LOCAL    GetNextToken (int cf)
  150. {
  151.     if (reject)
  152.     ACCEPT;
  153.     
  154.     else
  155.     {
  156.     symbol = ScanNextToken (cf);
  157.     ACCEPT;
  158.     }
  159.  
  160.     return symbol;
  161. }
  162.  
  163. /*
  164.  * Look at the next token from input
  165.  */
  166.  
  167. static int F_LOCAL    LookAtNextToken (int cf)
  168. {
  169.     if (!reject)
  170.     {
  171.     symbol = ScanNextToken (cf);
  172.     REJECT;
  173.     }
  174.  
  175.     return symbol;
  176. }
  177.  
  178. /*
  179.  * Parse the current input stack
  180.  */
  181.  
  182. static C_Op * F_LOCAL yyparse (void)
  183. {
  184.     C_Op    *t;        /* yyparse output */
  185.  
  186.     ACCEPT;
  187.     yynerrs = 0;
  188.  
  189. /* Check for EOF */
  190.  
  191.     if ((LookAtNextToken (ALLOW_KEYWORD | ALLOW_ALIAS)) == 0)
  192.     {
  193.     (t = NEWNODE)->type = TEOF;
  194.     DPRINT (1, ("yyparse: Create TEOF"));
  195.     }
  196.  
  197.     else
  198.     {
  199.     t = CommandList ();
  200.     CheckNextTokenIS (CHAR_NEW_LINE, 0);
  201.     }
  202.  
  203.     return t;
  204. }
  205.  
  206. /*
  207.  * Check a pipeline
  208.  */
  209.  
  210. static C_Op * F_LOCAL ScanPipeSyntax (int LexicalControlFlags)
  211. {
  212.     C_Op    *t, *p, *tl = NULL;
  213.  
  214.     if ((t = ScanSimpleCommand (LexicalControlFlags)) != (C_Op *)NULL)
  215.     {
  216.     while (GetNextToken (0) == CHAR_PIPE)
  217.     {
  218.         if ((p = ScanSimpleCommand (ALLOW_CONTINUATION)) == NULL)
  219.         SyntaxError ("no commands found following pipe");
  220.  
  221.         if (tl == NULL)
  222.         {
  223.         tl = SetupTreeNode (TPIPE, t, p, NOWORDS);
  224.         t = tl;
  225.         }
  226.  
  227.         else
  228.         {
  229.         tl->right = SetupTreeNode (TPIPE, tl->right, p, NOWORDS);
  230.         tl = tl->right;
  231.         }
  232.     }
  233.  
  234.     REJECT;
  235.     }
  236.  
  237.     return t;
  238. }
  239.  
  240. static C_Op * F_LOCAL ScanAndOrSyntax (void)
  241. {
  242.     C_Op    *t, *p;
  243.     int        c;
  244.  
  245.     t = ScanPipeSyntax (0);
  246.  
  247.     if (t != NULL)
  248.     {
  249.     while (((c = GetNextToken (0)) == PARSE_LOGICAL_AND) ||
  250.            (c == PARSE_LOGICAL_OR))
  251.     {
  252.         if ((p = ScanPipeSyntax (ALLOW_CONTINUATION)) == NULL)
  253.         SyntaxError ("no commands found following || or &&");
  254.  
  255.         t = SetupTreeNode ((c == PARSE_LOGICAL_AND) ? TAND : TOR, t, p,
  256.                    NOWORDS);
  257.     }
  258.  
  259.     REJECT;
  260.     }
  261.  
  262.     return t;
  263. }
  264.  
  265. static C_Op *F_LOCAL CommandList (void)
  266. {
  267.     C_Op    *t, *p, *tl = NULL;
  268.     int        c;
  269.  
  270.     if ((t = ScanAndOrSyntax ()) != NULL)
  271.     {
  272.     while (((c = GetNextToken (0)) == CHAR_SEPARATOR) ||
  273.            (c == CHAR_ASYNC) || (c == PARSE_COPROCESS) ||
  274.            ((AllowMultipleLines ||
  275.             (source->type == SSTRING) ||
  276.         (source->type == SALIAS)) && (c == CHAR_NEW_LINE)))
  277.     {
  278.         if ((c == CHAR_ASYNC) || (c == PARSE_COPROCESS))
  279.         {
  280.         c = (c == CHAR_ASYNC) ? TASYNC : TCOPROCESS;
  281.  
  282.         if (tl)
  283.             tl->right = SetupTreeNode (c, tl->right, NOBLOCK, NOWORDS);
  284.  
  285.         else
  286.             t = SetupTreeNode (c, t, NOBLOCK, NOWORDS);
  287.         }
  288.  
  289.         if ((p = ScanAndOrSyntax ()) == NULL)
  290.         return t;
  291.  
  292.         if (tl == NULL)
  293.         {
  294.         tl = SetupTreeNode (TLIST, t, p, NOWORDS);
  295.         t = tl;
  296.         }
  297.  
  298.         else
  299.         {
  300.         tl->right = SetupTreeNode (TLIST, tl->right, p, NOWORDS);
  301.         tl = tl->right;
  302.         }
  303.     }
  304.  
  305.     REJECT;
  306.     }
  307.  
  308.     return t;
  309. }
  310.  
  311. /*
  312.  * Handle IO re-direction
  313.  */
  314.  
  315. static IO_Actions * F_LOCAL SynchroniseIOList (int LexicalControlFlags)
  316. {
  317.     IO_Actions        *iop;
  318.  
  319.     if (LookAtNextToken (LexicalControlFlags) != PARSE_REDIR)
  320.     return (IO_Actions *)NULL;
  321.  
  322.     ACCEPT;
  323.     iop = yylval.iop;
  324.     CheckNextTokenIS (PARSE_WORD, 0);
  325.     iop->io_name = yylval.cp;
  326.  
  327.     if ((iop->io_flag & IOTYPE) == IOHERE)
  328.     {
  329.     if (*CurrentLexIdentifier != 0) /* unquoted */
  330.         iop->io_flag |= IOEVAL;
  331.  
  332.     SaveHereDocumentInfo (iop);
  333.     }
  334.  
  335.     return iop;
  336. }
  337.  
  338. static void F_LOCAL CheckNextTokenIS (int c, int LexicalControlFlags)
  339. {
  340.     int        got;
  341.  
  342.     if ((got = GetNextToken (LexicalControlFlags)) != c)
  343.     {
  344.     CompilingError ();
  345.     ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, LookUpToken (c),
  346.                LookUpToken (got));
  347.     }
  348. }
  349.  
  350. /*
  351.  * Handle Nested thingys - ( and {
  352.  */
  353.  
  354. static C_Op * F_LOCAL ScanNestedSyntax (int type, int mark)
  355. {
  356.     C_Op    *t;
  357.  
  358.     AllowMultipleLines++;
  359.     t = CommandList ();
  360.     CheckNextTokenIS (mark, ALLOW_KEYWORD);
  361.     AllowMultipleLines--;
  362.     return SetupTreeNode (type, t, NOBLOCK, NOWORDS);
  363. }
  364.  
  365. /*
  366.  * Handle a single command and its bits and pieces - IO redirection,
  367.  * arguments and variable assignments
  368.  */
  369.  
  370. static C_Op * F_LOCAL ScanSimpleCommand (int LexicalControlFlags)
  371. {
  372.     C_Op    *t;
  373.     int            c;
  374.     IO_Actions        *iop;
  375.     Word_B        *Arguments = (Word_B *)NULL;
  376.     Word_B        *Variables = (Word_B *)NULL;
  377.     Word_B        *IOactions = (Word_B *)NULL;
  378.  
  379. /* Allocate space for IO actions structures */
  380.  
  381.     if (AllowMultipleLines)
  382.     LexicalControlFlags = ALLOW_CONTINUATION;
  383.  
  384.     LexicalControlFlags |= ALLOW_KEYWORD | ALLOW_ALIAS;
  385.  
  386.     while ((iop = SynchroniseIOList (LexicalControlFlags)) != NULL)
  387.     {
  388.     if (WordBlockSize (IOactions) >= NUFILE)
  389.     {
  390.         CompilingError ();
  391.         ShellErrorMessage (LIT_2ManyRedir);
  392.     }
  393.  
  394.     IOactions = AddWordToBlock ((char *)iop, IOactions);
  395.     LexicalControlFlags &=~ ALLOW_CONTINUATION;
  396.     }
  397.  
  398.     switch (c = GetNextToken (LexicalControlFlags))
  399.     {
  400.     case 0:
  401.         CompilingError ();
  402.         ShellErrorMessage ("unexpected EOF");
  403.         return NULL;
  404.  
  405.     case CHAR_SEPARATOR:
  406.         REJECT;
  407.         (t = NEWNODE)->type = TCOM;
  408.         DPRINT (1, ("ScanSimpleCommand: Create TCOM"));
  409.         break;
  410.  
  411.     default:
  412.         REJECT;
  413.  
  414.         if (WordBlockSize (IOactions) == 0)
  415.         return (C_Op *)NULL;        /* empty line        */
  416.  
  417.         (t = NEWNODE)->type = TCOM;
  418.         DPRINT (1, ("ScanSimpleCommand: Create TCOM"));
  419.         break;
  420.  
  421.     case PARSE_WORD:
  422.     case PARSE_MDPAREN:
  423.         REJECT;
  424.         (t = NEWNODE)->type = TCOM;
  425.         DPRINT (1, ("ScanSimpleCommand: Create TCOM"));
  426.  
  427.         if (c == PARSE_MDPAREN)
  428.         {
  429.         ACCEPT;
  430.         Arguments = AddWordToBlock (MemoryDup (LIT_ODP, 5), Arguments);
  431.         CheckNextTokenIS (PARSE_WORD, MATHS_EXPRESSION);
  432.         Arguments = AddWordToBlock (yylval.cp, Arguments);
  433.         Arguments = AddWordToBlock (MemoryDup (LIT_CDP, 5), Arguments);
  434.         }
  435.  
  436.         while (1)
  437.         {
  438.         switch (LookAtNextToken (0))
  439.         {
  440.             case PARSE_REDIR:
  441.             if (WordBlockSize (IOactions) >= NUFILE)
  442.             {
  443.                 CompilingError ();
  444.                 ShellErrorMessage (LIT_2ManyRedir);
  445.             }
  446.  
  447.             IOactions = AddWordToBlock (
  448.                     (char *)SynchroniseIOList (0),
  449.                     IOactions);
  450.             break;
  451.  
  452. /*
  453.  * Word - check to see if this should be an argument or a variable,
  454.  * depending on what we've seen, the state of the k flag and an
  455.  * assignment in the word
  456.  */
  457.  
  458.             case PARSE_WORD:
  459.             ACCEPT;
  460.             if (((WordBlockSize (Arguments) == 0) ||
  461.                  FL_TEST (FLAG_ALL_KEYWORDS)) &&
  462.                 (strchr (CurrentLexIdentifier + 1, CHAR_ASSIGN) !=
  463.                     (char *)NULL))
  464.                 Variables = AddWordToBlock (yylval.cp, Variables);
  465.  
  466.             else
  467.                 Arguments = AddWordToBlock (yylval.cp, Arguments);
  468.  
  469.             break;
  470.  
  471.             case PARSE_MPAREN:
  472.             ACCEPT;
  473.  
  474.             if (WordBlockSize (Arguments) != 1)
  475.                 SyntaxError ("Too many function names");
  476.  
  477.             if (*CurrentLexIdentifier == 0)
  478.                 SyntaxError ("Bad function name");
  479.  
  480.             (t = NEWNODE)->type = TFUNC;
  481.             DPRINT (1, ("ScanSimpleCommand: Create TFUNC"));
  482.             t->str = StringCopy (CurrentLexIdentifier);
  483.             CheckNextTokenIS (CHAR_OPEN_BRACES,
  484.                       ALLOW_CONTINUATION | ALLOW_KEYWORD);
  485.             t->left = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);
  486.             return t;
  487.  
  488.             default:
  489.             goto Leave;
  490.         }
  491.         }
  492. Leave:
  493.         break;
  494.  
  495.     case CHAR_OPEN_PARATHENSIS:
  496.         t = ScanNestedSyntax (TPAREN, CHAR_CLOSE_PARATHENSIS);
  497.         break;
  498.  
  499.     case CHAR_OPEN_BRACES:
  500.         t = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);
  501.         break;
  502.  
  503. /*
  504.  * Format for:  [[ .....  ]]
  505.  */
  506.  
  507.     case PARSE_TEST:
  508.         (t = NEWNODE)->type = TCOM;
  509.         DPRINT (1, ("ScanSimpleCommand: Create TCOM"));
  510.         Arguments = AddWordToBlock (MemoryDup (LIT_ODB, 5), Arguments);
  511.  
  512.         while (GetNextToken (TEST_EXPRESSION) == PARSE_WORD)
  513.         {
  514.         Arguments = AddWordToBlock (yylval.cp, Arguments);
  515.  
  516.         if (strcmp (CurrentLexIdentifier , "]]") == 0)
  517.             break;
  518.         }
  519.  
  520.         break;
  521.  
  522. /*
  523.  * Format for:    select word in list do .... done
  524.  *         select word do .... done
  525.  *        for word in list do .... done
  526.  *         for word do .... done
  527.  */
  528.  
  529.     case PARSE_FOR:
  530.     case PARSE_SELECT:
  531.         (t = NEWNODE)->type = (c == PARSE_FOR) ? TFOR : TSELECT;
  532.         DPRINT (1, ("ScanSimpleCommand: Create TFOR/TSELECT"));
  533.         CheckNextTokenIS (PARSE_WORD, 0);
  534.         t->str = StringCopy (CurrentLexIdentifier);
  535.         AllowMultipleLines++;
  536.         t->vars = GetINWordList ();
  537.         t->left = GetDoDoneCommandList ();
  538.         AllowMultipleLines--;
  539.         break;
  540.  
  541.  
  542. /*
  543.  * Format for:    while command do ... done
  544.  *         until command do ... done
  545.  */
  546.  
  547.     case PARSE_WHILE:
  548.     case PARSE_UNTIL:
  549.         AllowMultipleLines++;
  550.         (t = NEWNODE)->type = (c == PARSE_WHILE) ? TWHILE : TUNTIL;
  551.         DPRINT (1, ("ScanSimpleCommand: Create TWHILE/TUNTIL"));
  552.         t->left = CommandList ();
  553.         t->right = GetDoDoneCommandList ();
  554.         AllowMultipleLines--;
  555.         break;
  556.  
  557. /*
  558.  * Format for:    case name in .... esac
  559.  */
  560.  
  561.     case PARSE_CASE:
  562.         (t = NEWNODE)->type = TCASE;
  563.         DPRINT (1, ("ScanSimpleCommand: Create TCASE"));
  564.         CheckNextTokenIS (PARSE_WORD, 0);
  565.         t->str = yylval.cp;
  566.         AllowMultipleLines++;
  567.         CheckNextTokenIS (PARSE_IN, ALLOW_KEYWORD | ALLOW_CONTINUATION);
  568.         t->left = CaseList ();
  569.         CheckNextTokenIS (PARSE_ESAC, ALLOW_KEYWORD);
  570.         AllowMultipleLines--;
  571.         break;
  572.  
  573. /*
  574.  * Format for:    if command then command fi
  575.  *        if command then command else command fi
  576.  *        if command then command elif command then ... else ... fi
  577.  */
  578.  
  579.     case PARSE_IF:
  580.         AllowMultipleLines++;
  581.         (t = NEWNODE)->type = TIF;
  582.         DPRINT (1, ("ScanSimpleCommand: Create TIF"));
  583.         t->left = CommandList ();
  584.         t->right = ThenPartList ();
  585.         CheckNextTokenIS (PARSE_FI, ALLOW_KEYWORD);
  586.         AllowMultipleLines--;
  587.         break;
  588.  
  589. /*
  590.  * Format for: time command
  591.  */
  592.  
  593.     case PARSE_TIME:
  594.         t = ScanPipeSyntax (ALLOW_CONTINUATION);
  595.         t = SetupTreeNode (TTIME, t, NOBLOCK, NOWORDS);
  596.         break;
  597.  
  598. /*
  599.  * Format for:  function name { .... }
  600.  */
  601.  
  602.     case PARSE_FUNCTION:
  603.         (t = NEWNODE)->type = TFUNC;
  604.         DPRINT (1, ("ScanSimpleCommand: Create TFUNC"));
  605.         CheckNextTokenIS (PARSE_WORD, 0);
  606.         t->str = StringCopy (CurrentLexIdentifier);
  607.         CheckNextTokenIS (CHAR_OPEN_BRACES,
  608.                   (ALLOW_CONTINUATION | ALLOW_KEYWORD));
  609.         t->left = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);
  610.         break;
  611.     }
  612.  
  613. /* Get any remaining IOactions */
  614.  
  615.     while ((iop = SynchroniseIOList (ALLOW_KEYWORD)) != NULL)
  616.     {
  617.     if (WordBlockSize (IOactions) >= NUFILE)
  618.     {
  619.         CompilingError ();
  620.         ShellErrorMessage (LIT_2ManyRedir);
  621.     }
  622.  
  623.     IOactions = AddWordToBlock ((char *)iop, IOactions);
  624.     }
  625.  
  626. /* Save the IOactions */
  627.  
  628.     if (WordBlockSize (IOactions) == 0)
  629.     t->ioact = (IO_Actions **)NULL;
  630.  
  631.     else
  632.     t->ioact = (IO_Actions **) GetWordList (AddWordToBlock (NOWORD,
  633.                                 IOactions));
  634.  
  635. /* If TCOM, save the arguments and variable assignments */
  636.  
  637.     if (t->type == TCOM)
  638.     {
  639.     t->args = GetWordList (AddWordToBlock (NOWORD, Arguments));
  640.     t->vars = GetWordList (AddWordToBlock (NOWORD, Variables));
  641.     }
  642.  
  643. /* Handle re-direction on other pipelines */
  644.  
  645.     else if ((t->type != TPAREN) && (t->ioact != (IO_Actions **)NULL))
  646.     {
  647.     C_Op        *t1 = t;
  648.  
  649.     (t = NEWNODE)->type = TPAREN;
  650.     DPRINT (1, ("ScanSimpleCommand: Create TPAREN"));
  651.     t->left = t1;
  652.     t->right = NOBLOCK;
  653.     t->args = NOWORDS;
  654.     t->vars = NOWORDS;
  655.     t->ioact = t1->ioact;
  656.     t1->ioact = (IO_Actions **)NULL;
  657.     }
  658.  
  659. /*
  660.  * We should probably release IOactions, Arguments and Variables if they
  661.  * are not used.  However, I don't think its necessary.  The release memory
  662.  * level should do it.
  663.  */
  664.  
  665.     return t;
  666. }
  667.  
  668.  
  669. /*
  670.  * Processing for the do grouping - do ... done
  671.  */
  672.  
  673. static C_Op * F_LOCAL GetDoDoneCommandList (void)
  674. {
  675.     int        c;
  676.     C_Op    *list;
  677.  
  678.     if ((c = GetNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_DO)
  679.     {
  680.     CompilingError ();
  681.     ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, "do",
  682.                LookUpToken (c));
  683.     }
  684.  
  685.     list = CommandList ();
  686.     CheckNextTokenIS (PARSE_DONE, ALLOW_KEYWORD);
  687.     return list;
  688. }
  689.  
  690.  
  691. /*
  692.  * Handle the then part of an if statement
  693.  */
  694.  
  695. static C_Op * F_LOCAL ThenPartList (void)
  696. {
  697.     C_Op    *t;
  698.  
  699.     if (GetNextToken (0) != PARSE_THEN)
  700.     {
  701.     REJECT;
  702.     return (C_Op *)NULL;
  703.     }
  704.  
  705.     (t = NEWNODE)->type = 0;
  706.     DPRINT (1, ("ThenPartList: Create dummy"));
  707.  
  708.     if ((t->left = CommandList ()) == (C_Op *)NULL)
  709.     SyntaxError ("no command found after then");
  710.  
  711.     t->right = ElsePartList ();
  712.     return t;
  713. }
  714.  
  715.  
  716. /*
  717.  * Handle the else part of an if statement
  718.  */
  719.  
  720. static C_Op * F_LOCAL ElsePartList (void)
  721. {
  722.     C_Op    *t;
  723.  
  724.     switch (GetNextToken (0))
  725.     {
  726.       case PARSE_ELSE:
  727.     if ((t = CommandList ()) == (C_Op *)NULL)
  728.         SyntaxError ("no commands associated with else");
  729.  
  730.     return t;
  731.  
  732.       case PARSE_ELIF:
  733.     (t = NEWNODE)->type = TELIF;
  734.     DPRINT (1, ("ElsePartList: Create TELIF"));
  735.     t->left = CommandList ();
  736.     t->right = ThenPartList ();
  737.     return t;
  738.  
  739.       default:
  740.     REJECT;
  741.     return (C_Op *)NULL;
  742.     }
  743. }
  744.  
  745.  
  746. /*
  747.  * Process the CASE statment
  748.  */
  749.  
  750. static C_Op * F_LOCAL CaseList (void)
  751. {
  752.     C_Op    *t = (C_Op *)NULL;
  753.     C_Op    *tl = (C_Op *)NULL;
  754.  
  755.     while ((LookAtNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_ESAC)
  756.     {
  757.     C_Op    *tc = CaseListEntries ();
  758.  
  759.     if (tl == (C_Op *)NULL)
  760.     {
  761.         t = tc;
  762.         (tl = tc)->right = (C_Op *)NULL;
  763.     }
  764.  
  765.     else
  766.     {
  767.         tl->right = tc;
  768.         tl = tc;
  769.     }
  770.     }
  771.  
  772.     return t;
  773. }
  774.  
  775.  
  776. /*
  777.  * Process an individual case entry: pattern) commands;;
  778.  */
  779.  
  780. static C_Op * F_LOCAL CaseListEntries (void)
  781. {
  782.     C_Op    *t;
  783.     int        LexicalControlFlags = ALLOW_CONTINUATION | ALLOW_KEYWORD;
  784.     Word_B    *Patterns = (Word_B *)NULL;
  785.  
  786.     (t = NEWNODE)->type = TPAT;
  787.     DPRINT (1, ("CaseListEntries: Create TPAT"));
  788.  
  789.     if (GetNextToken (LexicalControlFlags) != CHAR_OPEN_PARATHENSIS)
  790.     REJECT;
  791.  
  792.     else
  793.     LexicalControlFlags = 0;
  794.  
  795.     do
  796.     {
  797.     CheckNextTokenIS (PARSE_WORD, LexicalControlFlags);
  798.     Patterns = AddWordToBlock (yylval.cp, Patterns);
  799.     LexicalControlFlags = 0;
  800.     } while (GetNextToken (0) == CHAR_PIPE);
  801.  
  802.     REJECT;
  803.  
  804. /*
  805.  * Terminate the list of patterns
  806.  */
  807.  
  808.     t->vars = GetWordList (AddWordToBlock (NOWORD, Patterns));
  809.  
  810. /*
  811.  * Check for the terminating ), and get the command list
  812.  */
  813.  
  814.     CheckNextTokenIS (CHAR_CLOSE_PARATHENSIS, 0);
  815.  
  816.     t->left = CommandList ();
  817.  
  818.     if ((LookAtNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_ESAC)
  819.     CheckNextTokenIS (PARSE_BREAK, ALLOW_CONTINUATION | ALLOW_KEYWORD);
  820.  
  821.     return (t);
  822. }
  823.  
  824.  
  825. /*
  826.  * Handle the in words.... part of a for or select statement.  Get the
  827.  * words and build a list.
  828.  */
  829.  
  830. static char ** F_LOCAL GetINWordList (void)
  831. {
  832.     int        c;
  833.     Word_B    *Parameters = (Word_B *)NULL;
  834.  
  835. /*
  836.  * Check to see if the next symbol is "in".  If not there are no words
  837.  * following
  838.  */
  839.  
  840.     if ((c = GetNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_IN)
  841.     {
  842.     REJECT;
  843.     return NOWORDS;
  844.     }
  845.  
  846. /* Get the list */
  847.  
  848.     while ((c = GetNextToken (0)) == PARSE_WORD)
  849.     Parameters = AddWordToBlock (yylval.cp, Parameters);
  850.  
  851.     if ((c != CHAR_NEW_LINE) && (c != CHAR_SEPARATOR))
  852.     {
  853.     CompilingError ();
  854.     ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, "newline' or ';",
  855.                LookUpToken (c));
  856.     }
  857.  
  858. /* Are there any words found? */
  859.  
  860.     if (Parameters == (Word_B *)NULL)
  861.     return NOWORDS;
  862.  
  863.     return GetWordList (AddWordToBlock (NOWORD, Parameters));
  864. }
  865.  
  866. /*
  867.  * supporting functions
  868.  */
  869.  
  870. static C_Op * F_LOCAL SetupTreeNode (int type, C_Op *t1, C_Op *t2, char **wp)
  871. {
  872.     C_Op    *t;
  873.  
  874.     (t = NEWNODE)->type = type;
  875.     DPRINT (1, ("SetupTreeNode: Create %d", type));
  876.     t->left = t1;
  877.     t->right = t2;
  878.     t->vars = wp;
  879.     return t;
  880. }
  881.  
  882. /*
  883.  * Get and compile the next command from the user/file etc
  884.  */
  885.  
  886. C_Op    *BuildParseTree (Source *s)
  887. {
  888.     C_Op    *t;        /* yyparse output */
  889.  
  890.     yynerrs = 0;
  891.     AllowMultipleLines = 0;
  892.     source = s;
  893.  
  894.     t = yyparse ();
  895.  
  896.     if (s->type == STTY || s->type == SFILE)
  897.     s->str = null;            /* line is not preserved    */
  898.  
  899.     return yynerrs ? (C_Op *)NULL : t;
  900. }
  901.  
  902. /*
  903.  * Get a new tree leaf structure
  904.  */
  905.  
  906. static C_Op * F_LOCAL CreateTreeNode (void)
  907. {
  908.     C_Op    *t;
  909.  
  910.     if ((t = (C_Op *)AllocateMemoryCell (sizeof (C_Op))) == (C_Op *)NULL)
  911.     ShellErrorMessage ("command line too complicated");
  912.  
  913.     return t;
  914. }
  915.  
  916. /*
  917.  * List of keywords
  918.  */
  919.  
  920. static struct res {
  921.     char    *r_name;
  922.     int        r_val;
  923. } restab[] = {
  924.     { "for",    PARSE_FOR},        { "case",    PARSE_CASE},
  925.     { "esac",    PARSE_ESAC},        { "while",    PARSE_WHILE},
  926.     { "do",    PARSE_DO},        { LIT_done,    PARSE_DONE},
  927.     { "if",    PARSE_IF},        { "in",        PARSE_IN},
  928.     { "then",    PARSE_THEN},        { "else",    PARSE_ELSE},
  929.     { "elif",    PARSE_ELIF},        { "until",    PARSE_UNTIL},
  930.     { "fi",    PARSE_FI},        { "select",    PARSE_SELECT},
  931.     { "time",    PARSE_TIME},        { "function",    PARSE_FUNCTION},
  932.     { LIT_Test,    PARSE_TEST},
  933.     { "{",    CHAR_OPEN_BRACES},    { "}",        CHAR_CLOSE_BRACES},
  934.  
  935.     { (char *)NULL,    0},
  936.  
  937. /* Additional definitions */
  938.  
  939.     { "word",    PARSE_WORD},        { "&&",        PARSE_LOGICAL_AND},
  940.     { "||",    PARSE_LOGICAL_OR},    { "redirection",PARSE_REDIR },
  941.     { "(..)",    PARSE_MPAREN},        { "((...))",    PARSE_MDPAREN},
  942.     { "|&",    PARSE_COPROCESS},    { "newline",'\n'},
  943.  
  944.     { (char *)NULL,    0}
  945. };
  946.  
  947. int LookUpSymbol (char *n)
  948. {
  949.     struct res        *rp = restab;
  950.  
  951.     while ((rp->r_name != (char *)NULL) && strcmp (rp->r_name, n))
  952.     rp++;
  953.  
  954.     return rp->r_val;
  955. }
  956.  
  957. static char * F_LOCAL LookUpToken (int n)
  958. {
  959.     struct res        *rp = restab;
  960.     int            first = TRUE;
  961.  
  962.     while (TRUE)
  963.     {
  964.         if ((rp->r_name == (char *)NULL) && !first)
  965.     {
  966.         char     *cp = GetAllocatedSpace (4);
  967.  
  968.         if (cp == (char *)NULL)
  969.         return (char *)NULL;
  970.  
  971.         sprintf (cp, "%c", n);
  972.         return cp;
  973.     }
  974.  
  975.     else if (rp->r_name == (char *)NULL)
  976.         first = FALSE;
  977.  
  978.     else if (rp->r_val == n)
  979.         return rp->r_name;
  980.  
  981.     rp++;
  982.     }
  983. }
  984.  
  985. /*
  986.  * Syntax error
  987.  */
  988.  
  989. static void F_LOCAL SyntaxError (char *emsg)
  990. {
  991.     CompilingError ();
  992.     ShellErrorMessage ("%s - %s", LIT_SyntaxError, emsg);
  993. }
  994.  
  995. /*
  996.  * Duplicate a memory string
  997.  */
  998.  
  999. static char * F_LOCAL    MemoryDup (char *string, size_t length)
  1000. {
  1001.     char    *t;
  1002.  
  1003.     if ((t = AllocateMemoryCell (length)) == (char *)NULL)
  1004.     ShellErrorMessage ("Out of memory");
  1005.     
  1006.     return memcpy (t, string, length);
  1007. }
  1008.